/***************************************************************************
 *   Copyright (C) 2003 by Moris Ravasio                                   *
 *   moris_ravasio@hotmail.com                                             *
 *                                                                         *
 *   Copyright (C) 2007 by Matvey Kozhev                                   *
 *   sikon@lucidfox.org                                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "qinkdlg.h"
#include "aboutdlg.h"
#include "device.h"

#include <QMenu>
#include <QMessageBox>
#include <QSettings>
#include <QTextStream>
#include <QDebug>

QInkDlg::QInkDlg(QWidget* parent)
    : QDialog(parent), _errorCode(NO_PRINTER_FOUND)
{
    setupUi(this);
    setAttribute(Qt::WA_DeleteOnClose, false);
    
    QIcon icon(ICONFILE("128x128"));
    icon.addFile(ICONFILE("64x64"));
    icon.addFile(ICONFILE("48x48"));
    icon.addFile(ICONFILE("32x32"));
    icon.addFile(ICONFILE("24x24"));
    icon.addFile(ICONFILE("22x22"));
    icon.addFile(ICONFILE("16x16"));
    setWindowIcon(icon);
    
    grpError->hide();
    grpLevels->show();
    lblIcon->setPixmap(QPixmap(RESDIR "/app.png"));

    // make the window as compact as possible
    QSize suggestedSize = sizeHint();
    resize(QSize(width(), suggestedSize.height()));
    
    QRect desk;
    desk = QApplication::desktop()->geometry();
    move(desk.center() - rect().center());
    
    btnAbout->setIcon(getButtonIcon("help-about"));
    btnDetect->setIcon(getButtonIcon("printer"));
    btnUpdate->setIcon(getButtonIcon("view-refresh"));
    btnClose->setIcon(getButtonIcon("window-close"));
    
    initBarMap();
    initTrayIcon(icon);
    loadState();
}

QInkDlg::~QInkDlg()
{
}

void QInkDlg::initTrayIcon(const QIcon& icon)
{
    trayIcon = new QSystemTrayIcon(this);
    trayIcon->setIcon(icon);
    connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),    SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
    
    QMenu *menu = new QMenu();
    QAction *action;
    
    action = menu->addAction(getButtonIcon("view-restore"), tr("&Show/hide"));
    connect(action, SIGNAL(triggered()), SLOT(toggleVisibility()));
    
    menu->addSeparator();
    action = menu->addAction(getButtonIcon("help-about"), tr("&About"));
    connect(action, SIGNAL(triggered()), SLOT(btnAbout_clicked()));
    
    action = menu->addAction(tr("About &Qt"));
    connect(action, SIGNAL(triggered()), SLOT(aboutQt()));
    
    menu->addSeparator();
    action = menu->addAction(getButtonIcon("application-exit"), tr("&Exit"));
    connect(action, SIGNAL(triggered()), SLOT(exit()));
    
    trayIcon->setContextMenu(menu);
    trayIcon->show();
}

void QInkDlg::initBarMap()
{
    barMap.insert(CARTRIDGE_BLACK, barBlack);
    barMap.insert(CARTRIDGE_COLOR, barColor);
    barMap.insert(CARTRIDGE_CYAN, barCyan);
    barMap.insert(CARTRIDGE_MAGENTA, barMagenta);
    barMap.insert(CARTRIDGE_YELLOW, barYellow);
    barMap.insert(CARTRIDGE_PHOTOBLACK, barPhotoBlack);
    barMap.insert(CARTRIDGE_PHOTO, barPhoto);
    barMap.insert(CARTRIDGE_PHOTOCYAN, barPhotoCyan);
    barMap.insert(CARTRIDGE_PHOTOMAGENTA, barPhotoMagenta);
    barMap.insert(CARTRIDGE_PHOTOYELLOW, barPhotoYellow);

    groupMap.insert(CARTRIDGE_BLACK, grpBlack);
    groupMap.insert(CARTRIDGE_COLOR, grpColor);
    groupMap.insert(CARTRIDGE_CYAN, grpCyan);
    groupMap.insert(CARTRIDGE_MAGENTA, grpMagenta);
    groupMap.insert(CARTRIDGE_YELLOW, grpYellow);
    groupMap.insert(CARTRIDGE_PHOTOBLACK, grpPhotoBlack);
    groupMap.insert(CARTRIDGE_PHOTO, grpPhoto);
    groupMap.insert(CARTRIDGE_PHOTOCYAN, grpPhotoCyan);
    groupMap.insert(CARTRIDGE_PHOTOMAGENTA, grpPhotoMagenta);
    groupMap.insert(CARTRIDGE_PHOTOYELLOW, grpPhotoYellow);
}

void QInkDlg::loadState()
{
    QSettings settings(QSettings::IniFormat, QSettings::UserScope, "LucidFox", "QInk");
    
    if(settings.contains("device"))
        lastDevice.deserialize(settings.value("device").toString());
    
    bool visible = settings.value("visible", true).toBool();
    autoDetect();
    
    if(visible)
    {
        show();
        updateView();
    }
}

void QInkDlg::saveState()
{
    QSettings settings(QSettings::IniFormat, QSettings::UserScope, "LucidFox", "QInk");
    
    settings.setValue("device", lastDevice.serialize());
    settings.setValue("visible", isVisible());
    settings.sync();
}

void QInkDlg::trayActivated(QSystemTrayIcon::ActivationReason reason)
{
    if(reason == QSystemTrayIcon::DoubleClick)
        toggleVisibility();
}

void QInkDlg::toggleVisibility()
{
    if(isVisible())
    {
        hide();
    }
    else
    {
        show();
        query();
    }

    saveState();
}

void QInkDlg::aboutQt()
{
    qApp->aboutQt();
}

void QInkDlg::cmbDevices_activated(const QString &)
{
    lastDevice = devList[cmbDevices->currentIndex()];
    updateView();
}

void QInkDlg::btnAdd_clicked()
{}

void QInkDlg::btnDetect_clicked()
{
    // Auto detect printers
    autoDetect();
}

void QInkDlg::btnAbout_clicked()
{
    AboutDlg dlg(this);
    dlg.exec();
}

void QInkDlg::btnUpdate_clicked()
{
    updateView();
}

void QInkDlg::btnClose_clicked()
{
    close();
}

void QInkDlg::exit()
{
    delete trayIcon;
    saveState();
    qApp->quit();
}

void QInkDlg::autoDetect(void)
{
    Device::enumerate(devList);

    // Are there some printers?
    if (devList.empty())
    {
        // No printers detected: show error
        lastDevice = Device::invalid;
        query();
    }
    else
    {
        bool lastFound = false;
        
        for (int i = 0; i < devList.size(); i++)
        {
            if (lastDevice == devList[i])
            {
                lastFound = true;
                break;
            }
        }

        if (!lastFound)
        {
            lastDevice = devList[0];
        }
        
        query();
    }
}

/*!
    \fn QInkDlg::showError(void)
 */
void QInkDlg::showError(QString errStr)
{
    grpLevels->hide();
    grpError->show();
    lblErrorDescr->setText(errStr);
    resize(0, 0);
}

void QInkDlg::query()
{
    if(!(lastDevice == Device::invalid))
        _errorCode = lastDevice.update();
    
    updateTooltip();
    
    if(isVisible())
        updateView();
}

void QInkDlg::updateTooltip(void)
{
    if(lastDevice == Device::invalid)
    {
        trayIcon->setToolTip(tr("QInk: No printers detected"));
        return;
    }
    
    if(_errorCode == INKLEVEL_OK)
    {
        QString toolTip = lastDevice + "\n\n";
        
        for(QMap<int, QProgressBar *>::iterator it = barMap.begin(); it != barMap.end(); ++it)
        {
            int level = lastDevice.getInkLevel(it.key());
            
            if(level != -1)
            {
                QProgressBar *bar = it.value();
                toolTip += tr("%1: %2%\n").arg(bar->toolTip()).arg(level);
            }
        }
        
        trayIcon->setToolTip(toolTip.trimmed());
    }
    else
    {
        trayIcon->setToolTip(tr("QInk: Printer error, double click for details"));
    }
}

void QInkDlg::updateView(void)
{
    if(lastDevice == Device::invalid)
    {
        cmbDevices->setEnabled(false);
        btnUpdate->setEnabled(false);

        showError(tr("<b>No printers detected on this system</b><br /><br /> "
         "It seems that there are no printer plugged to this computer. "
         "Please verify the connections of your devices. You may also not have "
         "the required permissions to access the printer device file.<br /><br />"
         "Try adding yourself to the <b>lp</b> user group using your system's "
         "administration utility or the <b>groupadd</b> console command, "
         "and then log out and back in and restart QInk.<br /><br />"
         "Alternatively, you can run QInk as root.<br /><br />"
         "Click <b>Detect</b> to try again."));
        
        return;
    }
    
    cmbDevices->setEnabled(true);
    btnUpdate->setEnabled(true);
    cmbDevices->clear();

    for (int i = 0; i < devList.size(); i++)
    {
        cmbDevices->addItem(devList[i]);
    }
        
    for (int i = 0; i < devList.size(); i++)
    {
        if(lastDevice == devList[i])
            cmbDevices->setCurrentIndex(i);
    }
        
    grpError->hide();
    grpLevels->hide();
    grpColors->show();
    grpPhotoColors->show();
    
    // Hide all groups preventively
    QList<QWidget *> groupList = groupMap.values();
    
    for(QList<QWidget *>::iterator it = groupList.begin(); it != groupList.end(); ++it)
    {
        (*it)->hide();
    }
    
    if(_errorCode == INKLEVEL_OK)
    {
        for(QMap<int, QProgressBar *>::iterator it = barMap.begin(); it != barMap.end(); ++it)
        {
            int level = lastDevice.getInkLevel(it.key());
            
            if(level != -1)
            {
                QProgressBar *bar = it.value();
                bar->setValue(level);
                groupMap[it.key()]->show();
            }
        }
        
        if(
            lastDevice.isMissing(CARTRIDGE_COLOR) &&
            lastDevice.isMissing(CARTRIDGE_CYAN) &&
            lastDevice.isMissing(CARTRIDGE_MAGENTA) &&
            lastDevice.isMissing(CARTRIDGE_YELLOW))
        {
            grpColors->hide();
        }
        
        if(
           lastDevice.isMissing(CARTRIDGE_PHOTO) &&
           lastDevice.isMissing(CARTRIDGE_PHOTOCYAN) &&
           lastDevice.isMissing(CARTRIDGE_PHOTOMAGENTA) &&
           lastDevice.isMissing(CARTRIDGE_PHOTOYELLOW))
        {
            grpPhotoColors->hide();
        }
        
        grpLevels->show();
        resize(0, 0);
    }
    else
    {
        switch(_errorCode)
        {
        case DEV_PARPORT_INACCESSIBLE:
        case DEV_LP_INACCESSIBLE:
        case COULD_NOT_GET_DEVICE_ID:
        case DEV_USB_LP_INACCESSIBLE:
        case COULD_NOT_WRITE_TO_PRINTER:
        case COULD_NOT_READ_FROM_PRINTER:
        case ERROR:
        case UNKNOWN_PORT_SPECIFIED:
        case NO_DEVICE_CLASS_FOUND:
        case NO_CMD_TAG_FOUND:
        case NO_PRINTER_FOUND:
        case COULD_NOT_PARSE_RESPONSE_FROM_PRINTER:
            showError(tr("<b>Generic error</b><br><br>An error has occured communicating with the selected device. Click <b>Detect</b> and <b>Update</b> to refresh QInk status and try again. If the problem persists, e-mail me a description of the situation and all that can be helpful to me for reconstructing your execution environment."));
            break;

        case NO_INK_LEVEL_FOUND:
            showError(tr("<b>Cannot read ink level from device</b><br><br>Cannot retrieve ink levels from the selected device. Ensure your printer is switched on and click on <b>Update</b> to try again."));
            break;

        case PRINTER_NOT_SUPPORTED:
            showError(tr("<b>Unsupported printer!</b><br><br>Selected printer is not supported by this version of libinklevel. Take a look to <a href='http://home.arcor.de/markusheinz'>http://home.arcor.de/markusheinz</a> for a complete list of devices supported by your version of libinklevel."));
            break;
        }
    }
}
